#pragma once

#include "AbstractModel.h"
#include "Vertex.h"
#include "ColorRGBA.h"
#include "StdAfx.h"
#include "MinMaxBox.h"

#include <vector>
using std::vector;


class PolySurface : public AbstractModel, public MinMaxBox
{
	public:
		PolySurface( void )
            : surfaceColor( 1, 1, 1 )
            , drawInColor ( true )
        { }

		PolySurface( ColorRGBA eColor, ColorRGBA bColor )
            : surfaceColor( eColor )
            , drawInColor ( true )
        { }

		PolySurface( ColorRGBA eColor )
            : surfaceColor( eColor )
            , drawInColor ( true )
        { }

		virtual PolySurface& addVertex( Vertex& vertex )
        {
			this->setMaxX( max( vertex.getX( ), this->getMaxX( ) ) );
			this->setMaxY( max( vertex.getY( ), this->getMaxY( ) ) );
			this->setMaxZ( max( vertex.getZ( ), this->getMaxZ( ) ) );

			this->setMinX( min( vertex.getX( ), this->getMinX( ) ) );
			this->setMinY( min( vertex.getY( ), this->getMinY( ) ) );
			this->setMinZ( min( vertex.getZ( ), this->getMinZ( ) ) );

			vertexList.push_back( vertex );

//            if ( vertex == first vertex )
            //{
//            findNormal( );      // serious performance hit???
            //}

			return *this;
		}

		virtual void draw( ) const
        {
            if ( drawInColor )
            {
                glColor3f( surfaceColor.getR( ), surfaceColor.getG( ), surfaceColor.getB( ) );
            }
//            glBegin( GL_LINE_LOOP );
            glBegin( GL_POLYGON );
			for( vector<Vertex>::const_iterator v = vertexList.begin( );
                 v != vertexList.end( );
                 ++v)
            {
				v->draw( );
			}
			glEnd( );
		}

		virtual ~PolySurface( void ) { }

        virtual PolySurface& setSurfaceColor( const ColorRGBA& color )
        {
            surfaceColor = color;
            return *this;
        }

        virtual const ColorRGBA& getSurfaceColor( ) const
        {
            return surfaceColor;
        }

		virtual void drawVertexNormals( ) const
        {
			for( vector<Vertex>::const_iterator it = vertexList.begin( );
				 it != vertexList.end( );
                 ++it )
            {
				if ( it->hasNormal( ) )
                {
				    it->drawNormal( );
                }
			}
        }

        virtual void setNormal( const Normal& n )
        {
            normal = n;
        }

        virtual const Normal& getNormal( ) const
        {
            return normal;
        }

        virtual void enableColor( )
        {
            drawInColor = true;
        }
        
        virtual void disableColor( )
        {
            drawInColor = false;
        }

		virtual void findNormal( )
        {
            normal.setCoord( 0, 0, 0 );

            if ( vertexList.size( ) < 3 )
            {
                return;     // at least three vertices needed to define a plane
            }
            
            // TODO: improve robustness for collinear adjacent edges

            Point3d v1 = vertexList[1] - vertexList[0];
			Point3d v2 = vertexList[2] - vertexList[1];
            
            float x = v1.getY( ) * v2.getZ( ) - v2.getY( ) * v1.getZ( );
			float y = v1.getZ( ) * v2.getX( ) - v2.getZ( ) * v1.getX( );
			float z = v1.getX( ) * v2.getY( ) - v2.getX( ) * v1.getY( );

			normal.setCoord( x / sqrt(x*x + y*y + z*z) / 10,
                             y / sqrt(x*x + y*y + z*z) / 10,
                             z / sqrt(x*x + y*y + z*z) / 10 );
            findCenter( v1 );
            normal.setOrigin( v1 );
		}

    private:
		virtual void findCenter( Point3d& center )
        {
            center.setCoord( 0, 0, 0 );

            if ( vertexList.size( ) < 1 )
            {
                return;     // no vertices - no center
            }

            for ( vector<Vertex>::const_iterator it = vertexList.begin( );
                  it != vertexList.end( );
                  ++it )
            {
                center = center + *it;
            }

            center = center / (float)vertexList.size( );
		}


		vector<Vertex> vertexList;
        Normal         normal;
        ColorRGBA      surfaceColor;        
        bool           drawInColor;
};